﻿using System;
using System.Collections.ObjectModel;
using System.Linq;

// ReSharper disable CheckNamespace
namespace Equinoxe.EZNFC.Library
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// Tag information class
    /// </summary>
    /// <remarks></remarks>
    public abstract class TagInfo
    {
        /// <summary>
        /// Gets the number of keys.
        /// </summary>
        /// <remarks></remarks>
        public abstract int NumberOfKeys { get; }

        /// <summary>
        /// Gets the size of the key.
        /// </summary>
        /// <remarks></remarks>
        public abstract int KeySize { get; }

        /// <summary>
        /// Gets the number of blocks.
        /// </summary>
        /// <remarks></remarks>
        public abstract int NumberOfSectors { get; }

        /// <summary>
        /// Gets the type of the tag.
        /// </summary>
        /// <remarks></remarks>
        public abstract TagType TagType { get; }


        /// <summary>
        /// Gets the name of the tag.
        /// </summary>
        /// <remarks></remarks>
        public abstract string TagName { get; }

        /// <summary>
        /// Gets the manufacturer.
        /// </summary>
        /// <remarks></remarks>
        public abstract string Manufacturer { get; }

        /// <summary>
        /// Gets the tag capacity.
        /// </summary>
        /// <remarks></remarks>
        public virtual int Capacity
        {
            get
            {
                return BlocksInfo == null ? 0 : BlocksInfo.Sum(d => d.GetSectorsCapacity());
            }
        }

        /// <summary>
        /// Gets the blocks information.
        /// </summary>
        /// <remarks></remarks>
        public abstract ReadOnlyCollection<TagBlock> BlocksInfo { get; }

        /// <summary>
        /// Gets the block number from a sector and block.
        /// </summary>
        /// <param name="sectornumber">The sector number.</param>
        /// <param name="blocknumber">The block number.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public virtual int GetBlockNumber(int sectornumber, int blocknumber)
        {
            var blocksinfo = BlocksInfo.Where(d => d.StartSector <= sectornumber);

            var fullblocknumber = blocknumber;

            foreach (var blockinfo in blocksinfo)
            {
                if (blockinfo.EndSector > blocknumber)
                    fullblocknumber += (sectornumber - blockinfo.StartSector) * blockinfo.NumberOfBlocks;
                else
                    fullblocknumber += (blockinfo.EndSector - blockinfo.StartSector) * blockinfo.NumberOfBlocks;
            }

            return fullblocknumber;
        }

        /// <summary>
        /// Gets the number of blocks for a sector.
        /// </summary>
        /// <param name="sectornumber">The sector number.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public virtual int GetNumberOfBlocksForSector(int sectornumber)
        {
            if (sectornumber > NumberOfSectors)
                throw new IndexOutOfRangeException();

            var blockinfo = BlocksInfo.FirstOrDefault(d => (sectornumber >= d.StartSector) && (sectornumber <= d.EndSector));

            return blockinfo == null ? -1 : blockinfo.NumberOfBlocks;
        }

    }


    /// <summary>
    /// A block information
    /// </summary>
    /// <remarks></remarks>
    public class TagBlock
    {
        /// <summary>
        /// Gets the start sector.
        /// </summary>
        /// <remarks></remarks>
        public int StartSector { get; private set; }

        /// <summary>
        /// Gets the end sector.
        /// </summary>
        /// <remarks></remarks>
        public int EndSector { get; private set; }

        /// <summary>
        /// Gets the number of blocks per sector.
        /// </summary>
        /// <remarks></remarks>
        public int NumberOfBlocks { get; private set; }

        /// <summary>
        /// Gets the size of the block in bytes.
        /// </summary>
        /// <remarks></remarks>
        public int BlockCapacity { get; private set; }

        /// <summary>
        /// Gets the total sectors capacity.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public int GetSectorsCapacity()
        {
            return ((EndSector + 1) - StartSector) * NumberOfBlocks * BlockCapacity;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TagBlock"/> class.
        /// </summary>
        /// <param name="startSector">The start sector.</param>
        /// <param name="endSector">The end sector.</param>
        /// <param name="numberOfBlocks">The number of blocks.</param>
        /// <param name="blockCapacity">Capacity of a block.</param>
        /// <remarks></remarks>
        public TagBlock(int startSector, int endSector, int numberOfBlocks, int blockCapacity)
        {
            EndSector = endSector;
            NumberOfBlocks = numberOfBlocks;
            BlockCapacity = blockCapacity;
            StartSector = startSector;
        }
    }
}
